package top.guoziyang.mydb.backend.vm;

import top.guoziyang.mydb.backend.tm.TransactionManager;

/**
 * 管理事务中数据项的可见性判断,为数据库的事务隔离机制提供支持
 */
public class Visibility {

    /**
     * 判断在当前事务t的视角下是否需要跳过某个版本e（即是否要避免读取该版本的数据）
     */
    public static boolean isVersionSkip(TransactionManager tm, Transaction t, Entry e) {
        long xmax = e.getXmax();
        // 0表示 READ_COMMITTED（读已提交）,其他值表示可重复读
        // 读已提交隔离级别下，总是读取最新版本，不需要跳跃检查
        if(t.level == 0) {
            return false;
        } else {
            // 可重复读隔离级别，判断是否需要跳过该版本,返回true时需要跳过该版本(Entry)
            return tm.isCommitted(xmax) && (xmax > t.xid || t.isInSnapshot(xmax));
        }
    }

    /**
     * 根据事务的隔离级别判断某个Entry对当前事务t是否可见
     */
    public static boolean isVisible(TransactionManager tm, Transaction t, Entry e) {
        if(t.level == 0) {
            return readCommitted(tm, t, e);
        } else {
            return repeatableRead(tm, t, e);
        }
    }

    /**
     * 读已提交隔离级别下,判断一个Entry对事件t是否可见
     * 在该隔离级别下，事务在读取数据时, 只能读取已经提交事务产生的数据
     */
    private static boolean readCommitted(TransactionManager tm, Transaction t, Entry e) {
        long xid = t.xid;
        long xmin = e.getXmin();
        long xmax = e.getXmax();
        // 该Entry是当前事务创建的且未删除，可见
        if(xmin == xid && xmax == 0){
            return true;
        }
        // 或者，由一个已经提交的事务创建
        if(tm.isCommitted(xmin)) {
            // 且该Entry没有被删除，此时Entry对当前事务T可见
            if(xmax == 0) {
                return true;
            }
            //由其他事务删除，但是
            if(xmax != xid) {
                // 删除事务还未提交，则Entry对当前事务仍然可见
                if(!tm.isCommitted(xmax)) {
                    return true;
                }
            }
        }
        // 上述条件都不满足，不可见
        return false;
    }

    /**
     * 可重复读隔离级别下,判断一个Entry对事件t是否可见
     * 该隔离级别下，规定事务只能读取它开始时, 就已经结束的那些事务产生的数据版本
     * 因此事务需要忽略：
     * ①在本事务后开始的事务的数据;
     * ②本事务开始时还是 active 状态的事务的数据
     */
    private static boolean repeatableRead(TransactionManager tm, Transaction t, Entry e) {
        long xid = t.xid;
        long xmin = e.getXmin();
        long xmax = e.getXmax();
        // 该Entry是当前事务创建的且未删除，可见
        if(xmin == xid && xmax == 0){
            return true;
        }
        // 或者由一个已提交的事务创建,且该事务(指的是创建该记录的事务,xmin)的ID小于当前事务ID，
        // 并且该事务在在当前事务开始前就提交了，保证它不是活跃状态，这是为了防止xmin在事务快照中，但是在执行这段代码时又提交了。
        if(tm.isCommitted(xmin) && xmin < xid && !t.isInSnapshot(xmin)) {
            //满足上述条件的情况下，该Entry未被删除，则对当前事务可见
            if(xmax == 0){
                return true;
            }
            // 由其他事务删除，但是
            if(xmax != xid) {
                // 这个删除事务还未提交 或  这个删除事务在T开始之后才开始
                // 或者 这个删除事务在当前事务的事务快照中（活跃状态，即未提交）
                if(!tm.isCommitted(xmax) || xmax > xid || t.isInSnapshot(xmax)) {
                    return true;
                }
            }
        }
        return false;
    }

}
